JavaScript 学习笔记:面向对象之混入

在 JavaScript 中,基于原型的继承是不支持多继承的。但是,在有些时候我们希望有类似的机制。一个非常的典型的例子是事件。有很多的对象,都应该含有对事件处理的方法。而这些针对事件处理的方法不适合被放在原型链中。在 JavaScript 中,实现混入mixin的方式非常简单:将这些方法放在一个对象中,然后通过 Object.assign 将该对象内的方法添加到类中。

实例:事件混入

许多对象的一个重要特性是对事件的处理。下面的代码,通过混入的方式扩展了类的行为。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
//事件混入:
const eventMixin = {
/**
* 订阅事件
* @param {String} eventName 订阅的事件名称
* @param {Function} handler 订阅的事件处理的回调函数
*/
on(eventName, handler) {
if (!this._eventHandlers) {
this._eventHandlers = {};
}
if (!this._eventHandlers[eventName]) {
this._eventHandlers[eventName] = [];
}
this._eventHandlers[eventName].push(handler);
},

/**
* 取消订阅事件
* @param {String} eventName 要取消订阅的事件名称
* @param {Function} handler 要取消订阅的事件处理回调函数
*/
off(eventName, handler) {
const handlers = this._eventHandlers && this._eventHandlers[eventName];
if (!handlers) return;
for (let i = 0; i < handlers.length; i++) {
if (handlers[i] === handler) {
handlers.splice(i--, 1);
}
}
},

/**
* 触发事件
* @param {String} eventName 事件名称
* @param {any} args 事件处理回调函数的参数
*/
trigger(eventName, ...args) {
if (!this._eventHandlers || !this._eventHandlers[eventName]) {
return;
}
this._eventHandlers[eventName].forEach(handler => handler.apply(this, args));
}
};

class Menu {
choose(value) {
this.trigger("select", value);
}
}

// 将事件混入 Menu 对象的原型
Object.assign(Menu.prototype, eventMixin);

const menu = new Menu();
// 订阅 select 事件
menu.on("select", value => alert(value));

// 触发 select 事件
menu.choose("apple");